﻿using System;
using System.IO;
using System.Reflection;
using System.Xml;

namespace CXW.Core
{
    public class Runtime
    {
        string hintPath;
        string assembly;
        Assembly loadedAssembly = null;


        bool isAssemblyLoaded;
        readonly object lockObj = new object(); // used to protect mutable parts of runtime

        public Runtime(string assembly, string hintPath)
        {
            this.assembly = assembly;
            this.hintPath = hintPath;
        }

        /// <summary>
        /// Force loading the runtime assembly now.
        /// </summary>
        public void Load()
        {
            lock (lockObj)
            {
                if (!isAssemblyLoaded)
                {
                    isAssemblyLoaded = true;

                    try
                    {
                        string _path = Path.Combine(hintPath, assembly);
                        if (File.Exists(_path)) { loadedAssembly = System.Reflection.Assembly.LoadFrom(_path); }
                        else { loadedAssembly = System.Reflection.Assembly.LoadFrom(_path.Replace(@"AddIns" + Path.DirectorySeparatorChar, "")); }
                    }
                    catch (FileNotFoundException ex)
                    {
                        MessageService.ShowError("The addin '" + assembly + "' could not be loaded:\n" + ex.ToString());
                    }
                    catch (FileLoadException ex)
                    {
                        MessageService.ShowError("The addin '" + assembly + "' could not be loaded:\n" + ex.ToString());
                    }
                }
            }
        }

        public Assembly LoadedAssembly
        {
            get
            {
                Load(); // load the assembly, if not already done
                return loadedAssembly;
            }
        }

        public Type FindType(string className)
        {
            Assembly asm = LoadedAssembly;
            if (asm == null)
                return null;
            return asm.GetType(className);
        }

        internal static void ReadSection(XmlReader reader, AddIn addIn, string hintPath)
        {
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.EndElement:
                        if (reader.LocalName == "Runtime")
                        {
                            return;
                        }
                        break;
                    case XmlNodeType.Element:
                        switch (reader.LocalName)
                        {
                            case "Import":
                                addIn.Runtimes.Add(Runtime.Read(addIn, reader, hintPath));
                                break;
                            default:
                                throw new CoreException("Unknown node in runtime section :" + reader.LocalName);
                        }
                        break;
                }
            }
        }

        internal static Runtime Read(AddIn addIn, XmlReader reader, string hintPath)
        {
            if (reader.AttributeCount != 1)
            {
                throw new CoreException("Import node requires ONE attribute.");
            }
            return new Runtime(reader.GetAttribute(0), hintPath);
        }
    }
}
